﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SmartCollector.Domain;
using HtmlParser;
using System.Net;
using System.Drawing;
using SmartCollector.Domain.Entity;
using HtmlParser.SiteManager;

namespace SmartCollector.FilterManager
{
    public class FilterManager
    {
        #region Members
        private static FilterManager m_instance;
        private static object locker = new object();
        
        private int m_itemBoundaryCount = 0;

        private Scanner m_scanner = new Scanner();
        #endregion

        #region Properties
        public static FilterManager Instance
        {
            get
            {
                if (m_instance == null)
                {
                    lock (locker)
                    {
                        if (m_instance == null)
                        {
                            m_instance = new FilterManager();
                        }
                    }
                }
                return m_instance;
            }
        }
        #endregion

        #region Methods

        #region Public Methods

        /// <summary>
        /// Gets the new item.
        /// </summary>
        /// <param name="rssItem">The RSS item.</param>
        /// <param name="category">The category.</param>
        /// <param name="siteStructure">The site structure.</param>
        /// <returns></returns>
        public tbl_Item GetNewItem(RssReader.RssItem rssItem, int category,
            SiteItemStructure siteStructure, DateTime time)
        {
            tbl_Item rs = new tbl_Item();
            rs.CategoryID = category;
            rs.ReferenceSite = siteStructure.SiteId;
            rs.ImageUrl = FilterThumbImg(rssItem, siteStructure);
            string webpage = Helper.WebHelper.GetPageSource(rssItem.Link);
            m_scanner.HtmlParse(webpage);

            GoToItemBoudary(siteStructure);

            rs.Title = GetTitle(siteStructure);
            rs.Description = GetDescription(siteStructure);
            rs.Content = @GetContent(siteStructure);
            rs.Date = time;
            return rs;
        }

        #endregion

        #region Private Methods
        /// <summary>
        /// Gets the content.
        /// </summary>
        /// <param name="siteStructure">The site structure.</param>
        /// <returns></returns>
        private string GetContent(SiteItemStructure siteStructure)
        {
            bool isScript = false;
            bool hasAuthor = false;
            bool hasSource = false;
            int countAuthor = 0;
            int countSource = 0;

            TagSelector nearestBoundary = siteStructure.GetConstraint(RegionArea.NearestBoundary);
            TagSelector authorSelector = siteStructure.GetConstraint(RegionArea.Author);
            TagSelector sourceSelector = siteStructure.GetConstraint(RegionArea.Source);

            StringBuilder rs = new StringBuilder();
            while (m_scanner.HasNextTag() && m_itemBoundaryCount > 0)
            {
                HtmlTag tag = m_scanner.GetNextTag();
                tag.DomainName = siteStructure.SiteDomainName;
                #region Neu la Script thi loai bo tat ca nhung gi trong script
                if (tag.TagName != null && tag.TagName.ToLower().Trim() == "script")
                {
                    if (tag.IsEndTag == TagType.Open)
                    {
                        isScript = true;
                    }
                    else
                    {
                        isScript = false;
                        continue;
                    }
                }
                if (isScript)
                {
                    continue;
                }

                #endregion

                #region Stop if the iput tag
                if (tag.TagName == "input")
                {
                    break;
                }
                #endregion

                #region Comments some old Codes
                /*
                //if (!hasContent)
                //{
                //    if (siteStructure.Description.key == AttributeKey.Class)
                //    {
                //        if (tag.Attributes.Where(p => p.Name == "class" && p.Value == siteStructure.Description.Value).Count() > 0)
                //        {
                //            countContent += 1;
                //        }
                //    }
                //    else
                //    {
                //        if (tag.TagId == siteStructure.Description.Value)
                //        {
                //            countContent += 1;
                //        }
                //    }
                //}
                //if (hasContent)
                //{
                //    if (tag.IsEndTag == TagType.Open)
                //    {
                //        countContent += 1;
                //    }
                //    else if (tag.IsEndTag == TagType.Close)
                //    {
                //        countContent -= 1;
                //    }
                //}
                 * */
                #endregion

                #region Neu ra khoi Author
                if (hasAuthor)
                {
                    if (tag.IsEndTag == TagType.Close)
                    {
                        countAuthor--;
                    }
                    else
                    {
                        countAuthor++;
                    }
                    //Neu ra khoi Author 
                    // Va co source
                    if (countAuthor <= 0 &&
                        !CheckHasSource(5, siteStructure.GetConstraint(RegionArea.Source)))
                    {
                        break;
                    }
                }
                #endregion

                #region Check Author
                if (authorSelector != null && !hasAuthor)
                {
                    if (authorSelector.CheckIsThisTag(tag))
                    {
                        countAuthor++;
                        hasAuthor = true;
                    }
                }
                #endregion

                #region Count Source Tag depth
                if (hasSource)
                {
                    if (tag.IsEndTag == TagType.Close)
                    {
                        countSource--;
                    }
                    else
                    {
                        countSource++;
                    }
                    if (countSource <= 0)
                    {
                        break;
                    }
                }

                #endregion

                #region CheckSource
                if (sourceSelector != null && !hasSource)
                {
                    if (sourceSelector.CheckIsThisTag(tag))
                    {
                        countSource++;
                        hasSource = true;
                    }
                }
                #endregion

                #region Stop neu da den cai NearestItem
                if (nearestBoundary.CheckIsThisTag(tag) || m_itemBoundaryCount <= 0)
                {
                    break;
                }
                #endregion

                rs.Append(tag.ToStringWithoutAttribute());

                #region dem chieu sau tag boundaryContent
                if (tag.IsEndTag == TagType.Close)
                {
                    m_itemBoundaryCount -= 1;
                }
                else if (tag.IsEndTag == TagType.Open)
                {
                    m_itemBoundaryCount += 1;
                }
                #endregion
            }
            return rs.ToString();
        }

        /// <summary>
        /// Checks the has source.
        /// </summary>
        /// <param name="numberNextTag">The number next tag.</param>
        /// <param name="sourceSelector">The source selector.</param>
        /// <returns></returns>
        private bool CheckHasSource(int numberNextTag, TagSelector sourceSelector)
        {
            if (sourceSelector == null)
            {
                return false;
            }
            for (int i = 1; i < numberNextTag; i++)
            {
                HtmlTag tag = m_scanner.SeeNextTag(i);
                if (sourceSelector.CheckIsThisTag(tag))
                {
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// Gets the description.
        /// </summary>
        /// <param name="siteStructure">The site structure.</param>
        /// <returns></returns>
        private string GetDescription(SiteItemStructure siteStructure)
        {
            int countDescription = 0;
            bool hasDescription = false;
            StringBuilder rs = new StringBuilder();
            TagSelector selector = siteStructure.GetConstraint(RegionArea.Description);
            if (selector == null)
            {
                return string.Empty;
            }

            while (m_scanner.HasNextTag() && m_itemBoundaryCount > 0)
            {
                HtmlTag tag = m_scanner.GetNextTag();
                tag.DomainName = siteStructure.SiteDomainName;

                #region When we get into Description we count depth
                if (hasDescription)
                {
                    if (tag.IsEndTag == TagType.Open)
                    {
                        countDescription += 1;
                    }
                    else if (tag.IsEndTag == TagType.Close)
                    {
                        countDescription -= 1;
                    }

                }
                #endregion
                #region Check Open Tag of Description

                if (!hasDescription)
                {
                    if (selector.CheckIsThisTag(tag))
                    {
                        countDescription++;
                        hasDescription = true;
                    }
                }

                #endregion


                #region Get Content in Description Tag
                if (countDescription > 0)
                {
                    rs.Append(tag.ToStringWithoutFormated());
                }
                if (hasDescription && countDescription <= 0)
                {
                    break;
                }

                #endregion

                #region We count the boundaryItem
                if (tag.IsEndTag == TagType.Close)
                {
                    m_itemBoundaryCount -= 1;
                }

                else if (tag.IsEndTag == TagType.Open)
                {
                    m_itemBoundaryCount += 1;
                }
                #endregion
            }
            return rs.ToString();
        }

        /// <summary>
        /// Gets the title.
        /// </summary>
        /// <param name="siteStructure">The site structure.</param>
        /// <returns></returns>
        private string GetTitle(SiteItemStructure siteStructure)
        {
            int titleCount = 0;
            bool hasTitle = false;
            bool isScript = false;
            StringBuilder rs = new StringBuilder();
            TagSelector titleSelector = siteStructure.GetConstraint(RegionArea.Title);
            if (titleSelector == null)
            {
                return string.Empty;
            }
            while (m_scanner.HasNextTag() && m_itemBoundaryCount > 0)
            {
                HtmlTag tag = m_scanner.GetNextTag();
                tag.DomainName = siteStructure.SiteDomainName;

                #region Neu la Script thi loai bo tat ca nhung gi trong script
                if (tag.TagName != null && tag.TagName.ToLower().Trim() == "script")
                {
                    if (tag.IsEndTag == TagType.Open)
                    {
                        isScript = true;
                    }
                    else
                    {
                        isScript = false;
                    }
                }
                if (isScript)
                {
                    continue;
                }
                #endregion

                #region Go into Title Tags
                if (!hasTitle)
                {
                    if (titleSelector.CheckIsThisTag(tag))
                    {
                        titleCount++;
                    }
                }
                #endregion

                #region Count tag depth in Title tags
                if (hasTitle)
                {
                    if (tag.IsEndTag == TagType.Open)
                    {
                        titleCount += 1;
                    }
                    else if (tag.IsEndTag == TagType.Close)
                    {
                        titleCount -= 1;
                    }
                }
                #endregion
                if (titleCount > 0)
                {
                    rs.Append(tag.ToStringWithoutFormated());
                    hasTitle = true;
                }

                #region Count Tag in Boundary Items
                if (tag.IsEndTag == TagType.Close)
                {
                    m_itemBoundaryCount -= 1;
                }
                else if (tag.IsEndTag == TagType.Open)
                {
                    m_itemBoundaryCount += 1;
                }
                #endregion

                if (hasTitle && titleCount <= 0)
                {
                    break;
                }
            }
            return rs.ToString();
        }

        /// <summary>
        /// Goes to item boudary.
        /// </summary>
        /// <param name="siteStructure">The site structure.</param>
        private void GoToItemBoudary(SiteItemStructure siteStructure)
        {
            TagSelector boundarySelector = siteStructure.GetConstraint(RegionArea.Boundary);

            while (m_scanner.HasNextTag())
            {
                HtmlTag tag = m_scanner.GetNextTag();
                if (boundarySelector.CheckIsThisTag(tag))
                {
                    m_itemBoundaryCount += 1;
                    break;
                }
            }
        }

        //TODO: Implement get thumbnail Image from RssItem
        private string FilterThumbImg(RssReader.RssItem rssItem, SiteItemStructure siteStructure)
        {
            Scanner newScanner = new Scanner();
            newScanner.HtmlParse(rssItem.Description);
            HtmlTag imgTag = null;
            while (newScanner.HasNextTag())
            {
                HtmlTag tmp = newScanner.GetNextTag();
                if (tmp is ImgTag)
                {
                    imgTag = tmp;
                    break;
                }
            }
            return imgTag == null ? string.Empty : imgTag.Attributes.Where(p => p.Name.ToLower() == "src")
                .FirstOrDefault().Value;
            //TODO: return Call GetImgeFromSever

        }

        private string GetImageFromSever(string url)
        {
            WebRequest requestPic = WebRequest.Create(url);

            WebResponse responsePic = requestPic.GetResponse();

            Image webImage = Image.FromStream(responsePic.GetResponseStream());

            //TODO: implement save image to disk
            // and return the url for this image
            return string.Empty;
        }
        #endregion

        #endregion
    }
}
